/*
Pyxis 2.0
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Touch;

using GHIElectronics.NETMF.Hardware;

namespace Skewworks.Pyxis.EXT
{

    public static class SettingsManager
    {

        #region Enumerations

        public enum ScreenResolution
        {
            w320h240 = 0,
            w480h272 = 1,
            w800h480 = 2,
        }

        #endregion

        #region Constructor

        static SettingsManager()
        {
            LoadBootSettings();
            LoadLCDSettings();
            LoadUpdateSettings();
        }

        #endregion

        #region LCD EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SavedLCDSettings;

        private static Pyxis2LCDSettings _lcd;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2LCDSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2LCDSettings(ScreenResolution Resolution, ScreenCalibration iCalibrate, int iCalibrationPoints, short[] icalibrationSX, short[] icalibrationSY, short[] icalibrationCX, short[] icalibrationCY)
            {
                this.Resolution = Resolution;
                calibrateLCD = iCalibrate;
                calibrationPoints = iCalibrationPoints;
                calibrationSX = icalibrationSX;
                calibrationSY = icalibrationSY;
                calibrationCX = icalibrationCX;
                calibrationCY = icalibrationCY;
            }

            // Fields must be serializable.
            public ScreenResolution Resolution;
            public ScreenCalibration calibrateLCD;
            public int calibrationPoints;
            public short[] calibrationSX;
            public short[] calibrationSY;
            public short[] calibrationCX;
            public short[] calibrationCY;
        }

        #endregion

        #region Boot EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedBootSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SavedBootSettings;

        private static Pyxis2BootSettings _boot;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2BootSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2BootSettings(NetworkConnectionType ConnectionType, string IPAddress, string GatewayAddress, string SubnetMask, bool CheckUpdates, bool EnableRTC, byte Volume, byte TrebleAmp, byte TrebleLimit, byte BassAmp, byte BassLimit, int TimeZone)
            {
                this.ConnectionType = ConnectionType;
                this.IPAddress = IPAddress;
                this.GatewayAddress = GatewayAddress;
                this.SubnetMask = SubnetMask;
                this.CheckUpdates = CheckUpdates;
                this.EnableRTC = EnableRTC;
                this.Volume = Volume;
                this.TrebleAmp = TrebleAmp;
                this.TrebleLimit = TrebleLimit;
                this.BassAmp = BassAmp;
                this.BassLimit = BassLimit;
                this.TimeZone = TimeZone;
            }

            // Fields must be serializable.
            public NetworkConnectionType ConnectionType;
            public string IPAddress;
            public string GatewayAddress;
            public string SubnetMask;
            public bool CheckUpdates;
            public bool EnableRTC;
            public byte Volume;
            public byte TrebleAmp;
            public byte TrebleLimit;
            public byte BassAmp;
            public byte BassLimit;
            public int TimeZone;
        }

        #endregion

        #region Update EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedSysUpdateSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SystemUpdateSettings;

        private static Pyxis2SysUpdateSettings _update;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2SysUpdateSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2SysUpdateSettings(bool RequireUpdate, string FileLocation, int iResult)
            {
                this.NeedsUpdate = RequireUpdate;
                this.FilePath = FileLocation;
                this.LastResult = iResult;
            }

            // Fields must be serializable.
            public bool NeedsUpdate;
            public string FilePath;
            public int LastResult;
        }

        #endregion

        #region LCD Resolution

        /// <summary>
        /// Sets the LCD resolution ot 320x240
        /// </summary>
        public static void screen320x240(bool UpdateSettings = true)
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));

            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();

            lcdConfig.Width = 320;
            lcdConfig.Height = 240;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = false;
            lcdConfig.OutputEnableIsFixed = true;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.PixelPolarity = true;
            lcdConfig.HorizontalSyncPulseWidth = 41;
            lcdConfig.HorizontalBackPorch = 27;
            lcdConfig.HorizontalFrontPorch = 51;
            lcdConfig.VerticalSyncPulseWidth = 10;
            lcdConfig.VerticalBackPorch = 8;
            lcdConfig.VerticalFrontPorch = 16;
            lcdConfig.PixelClockDivider = 8;

            // Set config
            Configuration.LCD.Set(lcdConfig);

            if (UpdateSettings)
                UpdateLCDResolution(ScreenResolution.w320h240);
        }

        /// <summary>
        /// Sets the LCD resolution to 480x272
        /// </summary>
        public static void screen480x272(bool UpdateSettings = true)
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));

            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();
            lcdConfig.Width = 480;
            lcdConfig.Height = 272;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = false;
            lcdConfig.OutputEnableIsFixed = true;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.PixelPolarity = false;
            lcdConfig.HorizontalSyncPulseWidth = 41;
            lcdConfig.HorizontalBackPorch = 2;
            lcdConfig.HorizontalFrontPorch = 2;
            lcdConfig.VerticalSyncPulseWidth = 10;
            lcdConfig.VerticalBackPorch = 2;
            lcdConfig.VerticalFrontPorch = 2;

            // NOTE: This is used for EMX, comment if using ChipworkX.
            lcdConfig.PixelClockDivider = 8;

            // Set configs
            Configuration.LCD.Set(lcdConfig);

            if (UpdateSettings)
                UpdateLCDResolution(ScreenResolution.w480h272);

        }

        /// <summary>
        /// Sets the LCD resolution to 800x480
        /// </summary>
        public static void screen800x480(bool UpdateSettings = true)
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));

            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();
            lcdConfig.Width = 800;
            lcdConfig.Height = 480;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = true;
            lcdConfig.OutputEnableIsFixed = false;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.PixelPolarity = false;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.HorizontalSyncPulseWidth = 150;
            lcdConfig.HorizontalBackPorch = 150;
            lcdConfig.HorizontalFrontPorch = 150;
            lcdConfig.VerticalSyncPulseWidth = 2;
            lcdConfig.VerticalBackPorch = 2;
            lcdConfig.VerticalFrontPorch = 2;
            lcdConfig.PixelClockDivider = 4;

            // Set config
            Configuration.LCD.Set(lcdConfig);
            Configuration.Heap.SetCustomHeapSize(4 * 1024 * 1024);

            if (UpdateSettings)
                UpdateLCDResolution(ScreenResolution.w800h480);
        }

        private static void UpdateLCDResolution(ScreenResolution iRes)
        {
            if (_lcd == null)
            {
                _lcd = new Pyxis2LCDSettings(iRes, ScreenCalibration.Gather, 0, null, null, null, null);
            }
            else
            {
                _lcd.Resolution = iRes;
            }

            SavedLCDSettings.Target = _lcd;
        }

        #endregion

        #region Properties

        public static Pyxis2BootSettings BootSettings
        {
            get { return _boot; }
        }

        public static Pyxis2LCDSettings LCDSettings
        {
            get { return _lcd; }
        }

        public static Pyxis2SysUpdateSettings SystemUpdate
        {
            get { return _update; }
        }

        #endregion

        #region Public Methods

        public static bool SaveLCDCalibration(int iCalibrationPoints, short[] icalibrationSX, short[] icalibrationSY, short[] icalibrationCX, short[] icalibrationCY)
        {
            // Only allow default domain
            if (AppDomain.CurrentDomain.FriendlyName != "default") return false;

            // Don't set logo in emulator
            if (SystemInfo.SystemID.SKU == 3) return false;

            // Save calibration
            if (_lcd == null)
            {
                _lcd = new Pyxis2LCDSettings(ScreenResolution.w320h240, ScreenCalibration.Restore, iCalibrationPoints, icalibrationSX, icalibrationSY, icalibrationCX, icalibrationCY);
            }
            else
            {
                _lcd.calibrateLCD = ScreenCalibration.Restore;
                _lcd.calibrationPoints = iCalibrationPoints;
                _lcd.calibrationSX = icalibrationSX;
                _lcd.calibrationSY = icalibrationSY;
                _lcd.calibrationCX = icalibrationCX;
                _lcd.calibrationCY = icalibrationCY;
            }

            SavedLCDSettings.Target = _lcd;

            // Set calibration
            Touch.ActiveTouchPanel.SetCalibration(_lcd.calibrationPoints, _lcd.calibrationSX, _lcd.calibrationSY, _lcd.calibrationCX, _lcd.calibrationCY);

            return true;
        }

        public static bool RestoreLCDCalibration()
        {
            // Restore calibration data if we have it.
            try
            {
                if (_lcd != null)
                {
                    if (_lcd.calibrateLCD == ScreenCalibration.Restore)
                    {
                        Touch.ActiveTouchPanel.SetCalibration(_lcd.calibrationPoints, _lcd.calibrationSX, _lcd.calibrationSY, _lcd.calibrationCX, _lcd.calibrationCY);
                        return true;
                    }
                }
            }
            catch (Exception) { }

            return false;
        }

        #endregion

        #region Domain Locked Methods

        public static void RequireLCDCalibration()
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));

            if (_lcd != null)
                _lcd = new Pyxis2LCDSettings(_lcd.Resolution, ScreenCalibration.Gather, 0, null, null, null, null);
            else
                _lcd = new Pyxis2LCDSettings(ScreenResolution.w320h240, ScreenCalibration.Gather, 0, null, null, null, null);

            SavedLCDSettings.Target = _lcd;
        }

        public static void SaveLCDSettings(ScreenResolution Resolution, ScreenCalibration Calibration,  int iPoints, short[] SX, short[] SY, short[] CX, short[] CY)
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));
            _lcd = new Pyxis2LCDSettings(Resolution, Calibration, 0, null, null, null, null);
            SavedLCDSettings.Target = _lcd;
        }

        public static void SaveBootSettings(NetworkConnectionType ConnectionType, string IPAddress, string GatewayAddress, string SubnetMask, bool CheckUpdates, bool EnableRTC, byte Volume, byte TrebleAmp, byte TrebleLimit, byte BassAmp, byte BassLimit, int TimeZone)
        {
            if (AppDomain.CurrentDomain.FriendlyName != "default")
                throw new Exception(Resources.GetString(Resources.StringResources.DefaultDomainError));
            _boot = new Pyxis2BootSettings(ConnectionType, IPAddress, GatewayAddress, SubnetMask, CheckUpdates, EnableRTC, Volume, TrebleAmp, TrebleLimit, BassAmp, BassLimit, TimeZone);
            SavedBootSettings.Target = _boot;
        }

        #endregion

        #region Private Methods

        private static void LoadBootSettings()
        {
            // Grab the data
            SavedBootSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedBootSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SavedBootSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _boot = (Pyxis2BootSettings)SavedBootSettings.Target;

            if (_boot == null)
            {
                _boot = new Pyxis2BootSettings(NetworkConnectionType.DHCP, "", "", "", true, false, 100, 0, 0, 0, 0, -1);
                SavedBootSettings.Target = _boot;
            }

        }

        private static void LoadLCDSettings()
        {
            // Grab the data
            SavedLCDSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SavedLCDSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _lcd = (Pyxis2LCDSettings)SavedLCDSettings.Target;
        }

        private static void LoadUpdateSettings()
        {
            // Grab the data
            SystemUpdateSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedSysUpdateSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SystemUpdateSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _update = (Pyxis2SysUpdateSettings)SystemUpdateSettings.Target;
        }

        #endregion

    }

}
